package com.filetransfer.service;

import com.filetransfer.common.Packet;
import com.filetransfer.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class FileTransferService {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FileTransferService.class);
    
    private static final int WINDOW_SIZE = 5;           // 滑动窗口大小
    private static final int PACKET_SIZE = 64 * 1024;    // 数据包大小
    private static final int TIMEOUT_MS = 3000;          // 超时时间
    
    // 用于存储传输状态的map
    private final Map<String, TransferState> transferStates = new HashMap<>();
    
    /**
     * 开始文件传输
     */
    public void startTransfer(File file) {
        String fileName = file.getName();
        TransferState state = new TransferState(file);
        transferStates.put(fileName, state);
        
        log.info("开始传输文件: {}", fileName);
        log.info("文件大小: {}", FileUtils.formatFileSize(file.length()));
        
        // 模拟传输过程
        new Thread(() -> {
            try {
                simulateTransfer(fileName, state);
            } catch (Exception e) {
                log.error("传输文件失败: {}", e.getMessage(), e);
            }
        }).start();
    }
    
    /**
     * 模拟文件传输过程
     */
    private void simulateTransfer(String fileName, TransferState state) throws IOException, NoSuchAlgorithmException {
        File file = state.getFile();
        long fileSize = file.length();
        long startTime = System.currentTimeMillis();
        AtomicLong transferredBytes = new AtomicLong(0);
        
        // 模拟滑动窗口传输
        int seqId = 0;
        BlockingQueue<Integer> ackQueue = new LinkedBlockingQueue<>();
        
        // 开始传输统计线程
        Thread statsThread = startStatsThread(fileName, fileSize, transferredBytes, startTime);
        
        try {
            // 读取文件并模拟发送数据包
            byte[] buffer = new byte[PACKET_SIZE];
            long position = 0;
            
            while (position < fileSize) {
                // 发送窗口内的数据
                while (seqId - state.getLastAck() < WINDOW_SIZE && position < fileSize) {
                    int bytesToRead = (int) Math.min(PACKET_SIZE, fileSize - position);
                    // 模拟读取数据
                    // 在实际应用中，这里应该从文件中读取真实数据
                    byte[] data = new byte[bytesToRead];
                    
                    // 创建数据包
                    Packet packet = new Packet(Packet.TYPE_FILE_DATA, seqId, data);
                    
                    // 打印发送信息（模拟发送）
                    log.debug("发送数据包: seqId={}, length={}", seqId, bytesToRead);
                    
                    // 模拟接收方确认
                    int ackSeqId = seqId;
                    new Thread(() -> {
                        try {
                            // 模拟网络延迟
                            Thread.sleep(50 + (int)(Math.random() * 200));
                            ackQueue.put(ackSeqId);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }).start();
                    
                    position += bytesToRead;
                    seqId++;
                }
                
                // 等待确认
                Integer ack = ackQueue.poll(TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS);
                if (ack != null) {
                    state.setLastAck(ack);
                    transferredBytes.addAndGet(PACKET_SIZE);
                } else {
                    // 超时重传（简化处理，实际应该记录哪些包需要重传）
                    log.warn("数据包 {} 超时，需要重传", seqId - WINDOW_SIZE);
                }
            }
            
            // 发送结束包
            Packet finishPacket = new Packet(Packet.TYPE_FINISH, seqId, new byte[0]);
            log.debug("发送结束包: seqId={}", seqId);
            
            // 计算传输完成时间
            long endTime = System.currentTimeMillis();
            double duration = (endTime - startTime) / 1000.0;
            double averageSpeed = fileSize / duration;
            
            // 计算文件哈希值
            String md5 = FileUtils.calculateFileMD5(file);
            
            log.info("文件传输完成!");
            log.info("文件名: {}", fileName);
            log.info("文件大小: {}", FileUtils.formatFileSize(fileSize));
            log.info("总耗时: {}", String.format("%.2f 秒", duration));
            log.info("平均速度: {}", FileUtils.formatSpeed(averageSpeed));
            log.info("文件MD5: {}", md5);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("传输被中断");
        } finally {
            // 停止统计线程
            statsThread.interrupt();
            // 清理状态
            transferStates.remove(fileName);
        }
    }
    
    /**
     * 启动传输统计线程
     */
    private Thread startStatsThread(String fileName, long fileSize, AtomicLong transferredBytes, long startTime) {
        Thread statsThread = new Thread(() -> {
            try {
                while (!Thread.interrupted()) {
                    Thread.sleep(1000); // 每秒输出一次统计信息
                    
                    long currentTransferred = transferredBytes.get();
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    double currentSpeed = elapsedTime > 0 ? 
                            (currentTransferred * 1000.0) / elapsedTime : 0;
                    
                    log.info("[INFO]已传输: {} / {}, 速度: {}, 窗口大小: {}",
                            FileUtils.formatFileSize(currentTransferred),
                            FileUtils.formatFileSize(fileSize),
                            FileUtils.formatSpeed(currentSpeed),
                            WINDOW_SIZE);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        statsThread.setDaemon(true);
        statsThread.start();
        return statsThread;
    }
    
    /**
     * 传输状态内部类
     */
    private static class TransferState {
        private final File file;
        private int lastAck = -1;
        
        public TransferState(File file) {
            this.file = file;
        }
        
        public File getFile() {
            return file;
        }
        
        public int getLastAck() {
            return lastAck;
        }
        
        public void setLastAck(int lastAck) {
            this.lastAck = lastAck;
        }
    }
}